/* shift.c */

#include <avr/io.h>
#include "shift.h"

void shift_out_init(volatile uint8_t* ddr, volatile uint8_t latch, uint8_t clock, uint8_t data)
{
   *ddr = (1 << latch) | (1 << clock) | (1 << data);
}

void shift_out(volatile uint8_t* port, volatile uint8_t latch, uint8_t clock,
   uint8_t data, uint8_t c)
{
   *port &= ~(1 << latch);               // Latch low
   {
      *port &= ~(1 << data);             // Data low
      *port &= ~(1 << clock);            // Clock low

      for (int8_t i = 7; i >= 0; i--) {
         *port &= ~(1 << clock);         // Clock low
         if (c & (1 << i)) {
            *port |= (1 << data);        // Data high
         } else {
            *port &= ~(1 << data);       // Data low
         }
         *port |= (1 << clock);          // Clock high
         *port &= ~(1 << data);          // Data low
      }
      
      *port &= ~(1 << clock);           // Clock low
   }
   *port |= (1 << latch);               // Latch high
}

void shift_out_short(volatile uint8_t* port, volatile uint8_t latch, uint8_t clock,
   uint8_t data, uint16_t c)
{
   *port &= ~(1 << latch);               // Latch low
   {
      *port &= ~(1 << data);             // Data low
      *port &= ~(1 << clock);            // Clock low

      for (int8_t i = 15; i >= 0; i--) {
         *port &= ~(1 << clock);         // Clock low
         if (c & (1 << i)) {
            *port |= (1 << data);        // Data high
         } else {
            *port &= ~(1 << data);       // Data low
         }
         *port |= (1 << clock);          // Clock high
         *port &= ~(1 << data);          // Data low
      }
      
      *port &= ~(1 << clock);           // Clock low
   }
   *port |= (1 << latch);               // Latch high
}

void shift_out_string(volatile uint8_t* port, volatile uint8_t latch, uint8_t clock,
   uint8_t data, uint8_t* s, uint8_t l)
{
   *port &= ~(1 << latch);               // Latch low
   {
      while (l--)
      {
         uint8_t c = *s++;

         *port &= ~(1 << data);             // Data low
         *port &= ~(1 << clock);            // Clock low

         for (int8_t i = 7; i >= 0; i--) {
            *port &= ~(1 << clock);         // Clock low
            if (c & (1 << i)) {
               *port |= (1 << data);        // Data high
            } else {
               *port &= ~(1 << data);       // Data low
            }
            *port |= (1 << clock);          // Clock high
            *port &= ~(1 << data);          // Data low
         }
         
         *port &= ~(1 << clock);           // Clock low
      }
   }
   *port |= (1 << latch);               // Latch high
}
